﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MarvellousWorks.PracticalPatternNet8.Concept.Iterating;

public class CompositeIterator
{
    // 为每个可以遍历对象提供的容器
    // 由于类行为 object 所以 CompositeIterator 自身也可以嵌套
    private IDictionary<object, IEnumerator<ObjectWithName>> _iterator = new Dictionary<object, IEnumerator<ObjectWithName>>();

    public void Add(object name)
    {
        _iterator.Add(name, GetEnumerator(name));
    }

    /// <summary>
    /// 对外提供可以遍历的 IEnumerator
    /// </summary>
    /// <returns></returns>
    public IEnumerator<ObjectWithName> GetEnumerator()
    {
        if (_iterator != null && _iterator.Count > 0)
        {
            foreach (var item in _iterator.Values)
            {
                while (item.MoveNext())
                {
                    yield return item.Current;
                }
            }
        }
    }

    private IEnumerator<ObjectWithName> GetEnumerator(object name)
    {
        if (name == null) throw new ArgumentNullException("name");
        Type type = name.GetType();

        // 判断是否为 Stack
        if (type.IsAssignableFrom(typeof(Stack<ObjectWithName>)) || type.IsAssignableFrom(typeof(Stack)))
            return DynamicInvokeEnumerator(name);

        // 判断是否为 Queue
        if (type.IsAssignableFrom(typeof(Queue)) || type.IsAssignableFrom(typeof(Queue<ObjectWithName>)))
            return DynamicInvokeEnumerator(name);

        // 是否为 Array
        if ((type.IsArray) && (type.GetElementType()!.IsAssignableFrom(typeof(ObjectWithName))))
            return ((IEnumerable<ObjectWithName>)name).GetEnumerator();

        if (type.IsAssignableFrom(typeof(BinaryTreeNode)))
            return ((BinaryTreeNode)name).GetEnumerator();

        throw new NotImplementedException();
    }

    /// <summary>
    /// 通过反射动态调用相关实例的 GetEnumerator 方法获取 IEnumerator
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    private IEnumerator<ObjectWithName> DynamicInvokeEnumerator(object name)
    {
        if (name == null) throw new ArgumentNullException();
        Type type = name.GetType();
        return (IEnumerator<ObjectWithName>)type.InvokeMember("GetEnumerator", System.Reflection.BindingFlags.InvokeMethod, null, name, null)!;
    }
}
